import fs from 'node:fs/promises'
import path from 'node:path'
import type { RulensLinter } from '@/types/rulens.ts'
import { ResultAsync, err, ok } from 'neverthrow'
import { Logger } from '../utils/logger.ts'
import { lintRulesToMarkdown } from './lint-to-markdown.ts'

interface MarkdownGeneratorOptions {
  linters: RulensLinter[]
  outputFile: string
}

/**
 * Generate table of contents
 */
function generateTableOfContents(linters: RulensLinter[]): string {
  let toc = '## 📑 Table of Contents\n\n'

  toc += '- [Introduction](#introduction)\n'
  toc += '- [AI Usage Guide](#ai-usage-guide)\n'

  // 各Linterに対して目次エントリを追加
  for (const linter of linters) {
    const anchorName = `${linter.name.toLowerCase()}-rules`
    toc += `- [${linter.name} Rules](#${anchorName})\n`
    // It's possible to enumerate all categories, but keeping it simple
  }

  return toc
}

/**
 * Generate document header
 */
function generateHeader(): string {
  return '# Project Lint Rules Reference\n\n## 📋 Document Overview\n\nThis document contains a comprehensive catalog of linting rules enabled in this project. It is automatically generated by [Rulens](https://github.com/MH4GF/rulens) and provides AI code assistants and developers with detailed information about code style and quality requirements.\n\n'
}

/**
 * Generate introduction
 */
function generateIntroduction(): string {
  return (
    '## 📖 Introduction\n\n' +
    'This document lists all active linting rules configured in the project. Each rule includes:\n\n' +
    '- A link to official documentation\n' +
    '- A brief description of what the rule enforces\n' +
    '- Severity level (when available)\n\n' +
    'Use this reference to understand the code standards and avoid common issues when writing or reviewing code.\n\n'
  )
}

/**
 * Generate AI usage guide
 */
function generateAIUsageGuide(): string {
  return (
    '## 🤖 AI Usage Guide\n\n' +
    '**For AI Code Assistants**: When generating code for this project, please adhere to the following guidelines:\n\n' +
    `1. **Scan relevant categories first**: Focus on rules in categories related to the code you're generating.\n` +
    '2. **Respect all rules**: Ensure all generated code follows all linting rules.\n' +
    '3. **Avoid common pitfalls**: Check complexity rules to avoid anti-patterns.\n\n' +
    'When uncertain about specific rules, refer to the rule documentation links provided.\n\n'
  )
}

/**
 * Ensure directory exists before writing file
 * Returns ResultAsync with void on success or Error on failure
 */
function ensureDirectoryExists(filePath: string): ResultAsync<void, Error> {
  const directory = path.dirname(filePath)

  return ResultAsync.fromPromise(
    fs.mkdir(directory, { recursive: true }).then(() => undefined),
    (error): Error => (error instanceof Error ? error : new Error(String(error))),
  ).orElse((error) => {
    // Ignore error if directory already exists
    if ((error as NodeJS.ErrnoException).code === 'EEXIST') {
      return ok(undefined)
    }
    return err(error)
  })
}

/**
 * 純粋な関数：マークダウン文字列を生成する
 * テスト可能性を高めるために副作用を含まない
 */
export function generateMarkdownContent(options: {
  linters: RulensLinter[]
}): string {
  const { linters } = options

  // Generate each part of the document
  let markdown = generateHeader()
  markdown += '---\n\n'

  // Add table of contents
  markdown += generateTableOfContents(linters)
  markdown += '---\n\n'

  // Add introduction and AI usage guide
  markdown += generateIntroduction()
  markdown += '---\n\n'
  markdown += generateAIUsageGuide()
  markdown += '---\n\n'

  // 各Linterのルール情報をマークダウンに変換
  for (let i = 0; i < linters.length; i++) {
    const linter = linters[i]
    // 型チェックを追加して確実にRulensLinterであることを確認
    if (linter?.name && Array.isArray(linter.categories)) {
      markdown += `${lintRulesToMarkdown(linter, true)}`

      // 最後のlinter以外は改行を追加
      if (i < linters.length - 1) {
        markdown += '\n'
      }
    }
  }

  return markdown
}

/**
 * ファイルシステム操作を行う関数：マークダウンをファイルに書き込む
 */
export function writeMarkdownToFile(outputFile: string, content: string): ResultAsync<void, Error> {
  return ensureDirectoryExists(outputFile).andThen(() => {
    return ResultAsync.fromPromise(
      fs.writeFile(outputFile, content, 'utf-8'),
      (error): Error => (error instanceof Error ? error : new Error(String(error))),
    )
  })
}

/**
 * 既存の関数：マークダウンを生成してファイルに書き込む
 */
export function generateMarkdown(options: MarkdownGeneratorOptions): ResultAsync<string, Error> {
  const { linters, outputFile } = options
  const logger = new Logger()

  logger.info(`Generating markdown for ${outputFile}...`)

  // マークダウン内容を生成
  const markdown = generateMarkdownContent({ linters })

  // ファイルに書き込み
  return writeMarkdownToFile(outputFile, markdown).map(() => markdown)
}
